package MySQL::Query::Extract::SELECT;

use strict;
use warnings;

use base qw(MySQL::Query::Extract);

use Carp;
use Data::Dumper;
use SQL::Builder::Select;
use SQL::Builder::Column;
use SQL::Builder::Function;

my $rule_class    = 'DBIx::MyParsePP::Rule';
my $token_class   = 'DBIx::MyParsePP::Token';

sub parse_tree     { return $_[0]->tree->tree }

sub process {
      my ($self , $sel , $extract) = @_;

      #print Dumper $extract;
	
      my $data;
      my @extract = qw(simple_ident_q select_alias sum_expr simple_expr);
	
      my $select_tree = $self->parse_tree->getSelectItems();
      $select_tree = $select_tree->extract('select_part2');
      $select_tree = $select_tree->shrink;
      
      use Data::Dumper;
      #print Dumper $select_tree;
      #die;
      
      $self->select_part2($select_tree , $sel , $extract);
}

sub select_part2 {
      my ($self , $rule , $sel, $extract) = @_;
      
      my @children = $rule->children;
      
      foreach my $c ( @children ) {
            if ( $c->isa ( $rule_class ) ) {
                  my $method = $c->name;
                  next unless ($method eq 'select_item_list' or
                               $method eq 'select_item' or
                               $method eq 'sum_expr' or
                               $method eq 'simple_expr' or
                               $method eq 'simple_ident_q'
                              );
                  if ($method eq 'sum_expr') {
                        my $func = $self->$method($c , $sel , $extract);
                        $sel->cols->list_push($func) if $func;
                  } elsif ($method eq 'simple_ident_q') {
                        my @column = $self->$method($c , $sel, $extract);
                        my $col = SQL::Builder::Column->new();
                        $col->col(pop @column);
                        $col->other->list_push(@column);
                        $self->_check_for_extract($sel , $col , $extract);
                  }
                  else {
                        $self->$method($c , $sel , $extract);
                  }
                  next;
            }
            die "don't know what to do with $c" unless $c->isa( $token_class );
            
            #else it's just a column name
            
            my $type = $c->type; 
            if ($type eq 'IDENT') {
                  my $col = SQL::Builder::Column->new('col' => $c->toString);
                  $self->_check_for_extract($sel , $col , $extract);
            }
      }
}

sub select_item_list {
      my ($self , $rule, $sel, $extract) = @_;
      
      foreach my $c ( $rule->children ) {
            if ( $c->isa ( $rule_class ) ) {
                  my $method = $c->name;
                  if ($method eq 'simple_ident_q') {
                        my @column = $self->$method($c , $sel, $extract);
                        my $col = SQL::Builder::Column->new();
                        $col->col(pop @column);
                        $col->other->list_push(@column);
                        $self->_check_for_extract($sel , $col , $extract);
                  } elsif ($method =~ /simple_expr|sum_expr/) {
                        my $func = $self->$method($c , $sel , $extract);
                        $sel->cols->list_push($func) if $func;
                  }
                  else {
                        $self->$method($c , $sel , $extract);
                  }
                  next;
            }
            die "don't know what to do with $c" unless $c->isa( $token_class );
            
            #else it's just a column name
            my $type = $c->type; 
            if ($type eq 'IDENT') {
                  my $col = SQL::Builder::Column->new('col' => $c->toString);
                  $self->_check_for_extract($sel , $col , $extract);
            }
      }
}

sub select_item {
      my ($self , $rule , $sel, $extract) = @_;
      
      my $func;
      my $col = SQL::Builder::Column->new();     
      
      foreach my $c ( $rule->children ) {
            if ( $c->isa ( $rule_class ) ) {
                  my $method = $c->name;
                  if ($method eq 'simple_ident_q') {
                        my @column = $self->$method($c , $sel, $extract);
                        $col->col(pop @column);
                        $col->other->list_push(@column);
                  } elsif ($method eq 'simple_expr' or $method eq 'sum_expr') {
                        $func = $self->$method($c , $sel , $extract);
                  } elsif ($method eq 'select_alias') {
                        # it could be alias for either function or for row
                        my $alias = $self->$method($c , $sel , $extract);
                        if ($func) {
                              $func->alias($alias);
                        } elsif ($col) {
                              $col->alias($alias);
                        }
                  } else {
                        die "unhandled rule $method inside select_item"
                  }
                  next;
            }
            
            my $type = $c->type; 
            if ($type eq 'IDENT') {
                  unless ($col->col) {
                        $col->col($c->toString);
                  } else {
                        $col->alias($c->toString);
                  }
            }
      }
      
      if ($col->col) {
            $self->_check_for_extract($sel , $col , $extract);
      }
      
      if ($func) {
            $sel->cols->list_push($func);
      } 
}

sub select_alias {
      my ($self , $rule , $sel, $extract) = @_;
      
      foreach my $c ( $rule->children ) {
            die "there could be only tokens inside select_alias rule"
                  if $c->isa ( $rule_class );
                  
            my $type = $c->type;
            if ($type eq 'IDENT' or $type eq 'TEXT_STRING') {
                  return $c->toString;
            }
      }
}

sub simple_ident_q {
      my ($self , $rule , $sel, $extract) = @_;
      
      my @res;
      foreach my $c ( $rule->children ) {
            die "there could be only tokens inside simple_ident_q rule"
                  if $c->isa ( $rule_class );
            
            if ($c->type eq 'IDENT') {
                  push @res , $c->toString;
            }
      }
      
      return @res;
}

sub sum_expr { #COUNT , MAX , etc()
      my ($self , $rule , $sel , $extract) = @_;
      
      my $func = SQL::Builder::Function->new();
      foreach my $c ( $rule->children ) {
            if ( $c->isa ( $rule_class ) ) {
                  my $method = $c->name;
                  if ($method eq 'simple_ident_q') {
                        my @res = $self->$method($c , $sel , $extract);
                        # $self->_check_for_extract;
                        if (grep { $res[0] =~ /^$_ $/ } @$extract) {
                              $func->args->list_push(join '.' , @res);
                        }
                  } elsif ($method eq 'simple_expr') {
                        my $f = $self->$method($c , $sel , $extract);
                        $func->args->list_push($f);
                  } else {
                        die "Unknown rule $method in sum_expr";
                  }
                  next;
            }
            die "don't know what to do with $c"
                  unless $c->isa( $token_class );
            
            my $type = $c->type;
            if ($type =~ /^.+_SYM$/) {
                  $func->func($c->toString);
            } elsif ($type eq 'IDENT') {
                  $func->args->list_push($c->toString);
            } elsif ($type eq 'DISTINCT') {
                  $func->pre('DISTINCT ');
            }
      }
      
      if (ref $func->args->list eq 'ARRAY' and
          @{$func->args->list}
      ) {
            return $func;
      }
      
      
}

sub simple_expr {
      my ($self , $rule , $sel , $extract) = @_;
      
      my @children = $rule->children;
      my $function = shift @children;
     
      my $func = SQL::Builder::Function->new();
      
      #die "only one param functions are supported for now"
            #unless ($function->type eq 'FUNC_ARG1');
      
      foreach my $c ( @children ) {
            if ( $c->isa ( $rule_class ) ) {
                  my $method = $c->name;
                  if ($method eq 'simple_ident_q') {
                        my @res = $self->$method($c , $sel , $extract);
                        # $self->_check_for_extract;
                        if (grep { $res[0] =~ /^$_ $/ } @$extract) {
                              $func->args->list_push(join '.' , @res);
                        }
                  } elsif ($method eq 'expr_list2') {
                        my $params = $self->$method($c , $sel , $extract);
                        foreach my $res (@$params) {
                              if (grep { $res->[0] =~ /^$_ $/ } @$extract) {
                                    $func->args->list_push(join '.' , @$res);
                              }
                        }
                  } else {
                        die "Unknown rule $method in simple_expr";
                  }
                  next;
            }
            die "don't know what to do with $c"
                  unless $c->isa( $token_class );
                  
            my $type = $c->type; 
            if ($type eq 'IDENT') {
                  $func->args->list_push($c->toString);
            }
      }
      
      if (ref $func->args->list eq 'ARRAY' and
          @{$func->args->list}
      ) {
            $func->func($function->toString);
            return $func;
      }
      #FUNC_ARG1FUNC_ARG1
}

sub expr_list2 {
      my ($self , $rule , $sel , $extract) = @_;
      
      my @params = ();
      foreach my $c ( $rule->children ) {
            if ( $c->isa ( $rule_class ) ) {
                  my $method = $c->name;
                  if ($method eq 'simple_ident_q') {
                        my @res = $self->$method($c , $sel , $extract);
                        push @params , \@res;
                  } else {
                        die "Unknown rule $method in expr_list2";
                  }
                  next;
            }
            die "don't know what to do with $c"
                  unless $c->isa( $token_class );
                  
      }
      
      return \@params;
}

sub table_wild {
      my ($self , $rule , $sel, $extract) = @_;
      
      my @res;
      foreach my $c ( $rule->children ) {
            die "there could be only tokens inside simple_ident_q rule"
                  if $c->isa ( $rule_class );
            
            if ($c->type eq 'IDENT' or $c->type eq '*') {
                  push @res , $c->toString;
            }
      }
      
      return @res;
}

# ADD generic $extract check!
sub _check_for_extract {
      my ($self , $sel , $col , $extract) = @_;
      
      if ($extract) {
            my $table = $col->other->list;
            if (@$table) {
                  if (grep { $table->[0] =~ /^$_ $/ } @$extract) {
                        $sel->cols->list_push($col);
                  }
            }
            
            if (!@$table) {
                  # here we could lookup table structure
                  # and add $col only if $col->col belongs to table
                  $sel->cols->list_push($col);
            }
            
      } else {
            $sel->cols->list_push($col);
      }
}



1;

=head1 NAME

MySQL::Query::Extract::SELECT

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 METHODS

=head2 new()

=head2 process()
